package org.bultreebank.index.utils;

import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.StringTokenizer;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocCollector;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;


/**
 * Utilities used for searching 
 * @author Aneliya Ticheva
 *
 */

public class SearchUtils implements Constants{
	
	private static String cutLastChildContextPart(String childContextDocumentID)
	{
		int i = childContextDocumentID.indexOf(SPEC_CHAR);
		return childContextDocumentID.substring(0, i);
	}
	public static Set<String> getParentDocumentIDs(Set<String> matchingChildDocumentIDs)
	{
		Set<String> result = new HashSet<String>();
		Iterator<String> matchingDocIter = matchingChildDocumentIDs.iterator();
		while (matchingDocIter.hasNext())
		{
			String docID = matchingDocIter.next();
			docID = cutLastChildContextPart(docID);
			result.add(docID);
		}
		return result;
	}
	/**
	 * Searches lucene queries (queryString) in the specified field 
	 * @param openedReader
	 * @param queryString
	 * @param field - the searchable field
	 * @return - the document IDs that match the queryString
	 * @throws Exception
	 */
	public static Set<String> luceneFieldsSearch(Analyzer analyzer, 
			String indexDIR,
			String queryString, String field, boolean searchable, boolean keyword)
	throws Exception
	{
		if (!searchable)
			throw new Exception("The field you are searching in is not searchable!");
		if (!keyword)
			queryString = transformSimpleQuery(queryString, field);
		Set<String> matchingDocIDs = searchLuceneIndex(analyzer, indexDIR,
				queryString, field, keyword);
		return matchingDocIDs;
	}
	/**
	 * Searches in the index, that the reader is associated to, with the 
	 * specified queryString, in the indexed field (searchableField)
	 * @param openedReader
	 * @param queryString
	 * @param searchableField - the field that would be searched upon
	 * @return
	 * @throws Exception
	 *
	 */
	public static Set<String> searchLuceneIndex(Analyzer analyzer,
			  String indexDIR,
			  String queryString,
			  String searchableField, boolean keyword) throws Exception
	{
		Set<String> matchingDocIDs = new LinkedHashSet<String>();
		Searcher searcher = new IndexSearcher(FSDirectory.getDirectory(indexDIR));
		ScoreDoc[] hits = null;
	    if (keyword)
	    {
	    	
	    	TermQuery q = new TermQuery(new Term(searchableField, queryString));
	    	TopDocs docs = searcher.search(q, 10);
	    	hits = docs.scoreDocs;
	    }
	    else
	    {
	    	QueryParser parser = new QueryParser(searchableField, analyzer);
	    	if (queryString == null || queryString.length() == -1 || queryString.equals(EMPTY_STR))
	    		return null;
	    	Query query = parser.parse(queryString);
	    	TopDocCollector collector = new TopDocCollector(NUM_HITS);
	    	searcher.search(query, collector);
	    	hits = collector.topDocs().scoreDocs;
	    }
	    for (int i = 0; i < hits.length; i++)
	    {
	      int docId = hits[i].doc;
	      Document d = searcher.doc(docId);
	      String docName = d.get(DOC_ID);
	      matchingDocIDs.add(docName);
	    }
	   return matchingDocIDs;		   
 }
	
    public static Document getIndexedLuceneDocument(IndexReader openedReader, String docID, String fieldID)
    throws Exception
    {
    	ScoreDoc[] hits = null;
	   	TermQuery q = new TermQuery(new Term(DOC_ID, docID));
	   	Searcher searcher = new IndexSearcher(openedReader);
	   	TopDocs docs = searcher.search(q, 10);
	   	hits = docs.scoreDocs;
	    for (int i = 0; i < hits.length; i++)
	    {
	      int docId = hits[i].doc;
	      Document d = searcher.doc(docId);
	      return d;
	   }
	    return null;
    }
	
	/**
	 * Normalizes the query, for already extracted field
	 * @param sourceQuery - the query string without the specified field
	 * @return normalized string of the query
	 */
	public static String transformSimpleQuery(String sourceQuery,
			String field)
	throws Exception
	{
	     StringTokenizer st = null;
	     StringBuffer buf = new StringBuffer();
	   	 st = new StringTokenizer(sourceQuery);
			 while (st.hasMoreTokens()){
		            String tok = st.nextToken();
		            if (tok.equalsIgnoreCase(AND_SIGN)||tok.equalsIgnoreCase(OR_SIGN)||
		            		tok.equalsIgnoreCase(NOT_SIGN)
		            		|| tok.equalsIgnoreCase(RANGE_SIGN))
		                buf.append(WHITESPACE + tok + WHITESPACE);
		            else 
		            	buf.append(Utils.normalizeTerm(
		            			tok));
		 }
	    return buf.toString();
	}
	/**
	 * Opens a reader for the index directory <indexDIR>
	 * 
	 * @param indexDIR -
	 *            index directory
	 * @return an opened reader
	 * @throws Exception
	 */
	public static IndexReader openReader(String realIndexID)throws Exception 
	{
		if (realIndexID.equals(EMPTY_STR))
			throw new Exception("There is not such an index!");
		File index = new File(realIndexID);
		if (!index.exists())
			throw new Exception("The index directory " + realIndexID + " specified doesn't exist!");
		Directory directory = FSDirectory.getDirectory(realIndexID);

		return IndexReader.open(directory);
	}
	public static void closeWriter(IndexReader reader)throws Exception
	{
		if (reader != null)
			  reader.close();
	}
}
